package com.wxpay.controller;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.wxpay.common.ApiResponse;
import com.wxpay.config.WxPayConfig;
import com.wxpay.service.OrderService;
import com.wxpay.service.WxPayService;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.PrivateKey;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author worlt
 * @Date 2025/4/21 下午10:29
 */
@RestController
@RequestMapping("WxPay")
public class WxPayController {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private WxPayService wxPayService;
    
    @Autowired
    private OrderService orderService;
    
    @Autowired
    private WxPayConfig wxPayConfig;

    /**
     * 小程序支付接口
     * @param data 请求参数
     * @return 返回支付所需参数
     */
    @RequestMapping("/miniPay")
    @ResponseBody
    public JSONObject miniPay(@RequestParam Map<String, String> data) {
        JSONObject response = ApiResponse.init();
        try {
            String body = data.get("body");
            String money = data.get("money");
            String openId = data.get("openId");

            if (StrUtil.isBlank(body)) {
                response = ApiResponse.fail("body is null");
                return response;
            }
            if (StrUtil.isBlank(money)) {
                response = ApiResponse.fail("money is null");
                return response;
            }
            if (StrUtil.isBlank(openId)) {
                response = ApiResponse.fail("openId is null");
                return response;
            }

            JSONObject minappPay = wxPayService.minappPay(body, money, openId);
            if (minappPay == null) {
                response = ApiResponse.fail("支付下单失败");
            } else {
                response = ApiResponse.success("订单创建成功，等待支付", minappPay);
            }
        } catch (Exception e) {
            e.printStackTrace();
            response = ApiResponse.fail("系统异常: " + e.getMessage());
        }

        return response;
    }
    
    /**
     * 微信支付回调接口
     * @param request HTTP请求
     * @return 处理结果
     */
    @RequestMapping("/callback")
    @ResponseBody
    public String payCallback(HttpServletRequest request) {
        logger.info("接收到微信支付回调");
        Map<String, String> responseMap = new HashMap<>();
        
        try {
            // 1. 获取必要的回调信息
            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String signature = request.getHeader("Wechatpay-Signature");
            String serialNo = request.getHeader("Wechatpay-Serial");
            
            // 2. 读取请求体
            StringBuilder sb = new StringBuilder();
            BufferedReader reader = request.getReader();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            String requestBody = sb.toString();
            logger.info("微信支付回调原始内容:{}", requestBody);
            
            // 3. 验证签名
            if (!verifySign(requestBody, timestamp, nonce, signature, serialNo)) {
                logger.error("微信支付回调验签失败");
                responseMap.put("code", "FAIL");
                responseMap.put("message", "验签失败");
                return buildResponseString(responseMap);
            }
            
            logger.info("微信支付回调验签成功");
            
            // 4. 解析回调数据
            com.alibaba.fastjson.JSONObject callbackJson = com.alibaba.fastjson.JSONObject.parseObject(requestBody);
            String eventType = callbackJson.getString("event_type");
            
            // 只处理支付成功的通知
            if (!"TRANSACTION.SUCCESS".equals(eventType)) {
                logger.info("非支付成功事件，类型：{}", eventType);
                responseMap.put("code", "SUCCESS");
                responseMap.put("message", "非支付成功事件已接收");
                return buildResponseString(responseMap);
            }
            
            // 5. 获取加密资源内容
            com.alibaba.fastjson.JSONObject resource = callbackJson.getJSONObject("resource");
            String ciphertext = resource.getString("ciphertext");
            String nonce64 = resource.getString("nonce");
            String associatedData = resource.getString("associated_data");
            
            // 6. 解密资源内容
            String resourceJson = decryptResource(ciphertext, nonce64, associatedData);
            logger.info("解密后的支付结果：{}", resourceJson);
            
            // 7. 解析支付结果
            com.alibaba.fastjson.JSONObject resourceObj = com.alibaba.fastjson.JSONObject.parseObject(resourceJson);
            String tradeState = resourceObj.getString("trade_state");
            String orderId = resourceObj.getString("out_trade_no"); // 商户订单号
            String transactionId = resourceObj.getString("transaction_id"); // 微信支付交易号
            String successTime = resourceObj.getString("success_time"); // 支付完成时间
            
            logger.info("支付回调 - 订单号:{}, 微信交易号:{}, 交易状态:{}, 支付时间:{}", 
                        orderId, transactionId, tradeState, successTime);
            
            // 8. 更新订单状态
            if ("SUCCESS".equals(tradeState)) {
                try {
                    boolean result = orderService.paySuccess(orderId, transactionId, successTime);
                    if (!result) {
                        logger.error("订单状态更新失败，订单号:{}", orderId);
                        // 业务处理失败，但应该告诉微信支付回调已收到，避免重复通知
                        responseMap.put("code", "SUCCESS");
                        responseMap.put("message", "回调已接收，但业务处理失败");
                        return buildResponseString(responseMap);
                    }
                    
                    logger.info("订单状态更新成功，订单号:{}", orderId);
                } catch (Exception e) {
                    logger.error("订单处理异常，订单号:{}，异常:{}", orderId, e.getMessage());
                    // 订单处理异常，但仍然告诉微信支付回调已收到
                    responseMap.put("code", "SUCCESS");
                    responseMap.put("message", "回调已接收，但业务处理异常");
                    return buildResponseString(responseMap);
                }
            } else {
                logger.warn("支付状态不是SUCCESS, 状态:{}", tradeState);
            }
            
            // 9. 返回成功响应
            responseMap.put("code", "SUCCESS");
            responseMap.put("message", "成功");
            
        } catch (Exception e) {
            logger.error("处理微信支付回调异常", e);
            responseMap.put("code", "FAIL");
            responseMap.put("message", "系统异常：" + e.getMessage());
        }
        
        return buildResponseString(responseMap);
    }
    
    /**
     * 构建微信支付回调响应字符串
     * @param responseMap 响应参数
     * @return 响应JSON字符串
     */
    private String buildResponseString(Map<String, String> responseMap) {
        return JSON.toJSONString(responseMap);
    }
    
    /**
     * 验证微信支付回调签名
     */
    private boolean verifySign(String body, String timestamp, String nonce, String signature, String serialNo) {
        try {
            // 加载商户私钥
            PrivateKey privateKey = PemUtil.loadPrivateKey(
                    new FileInputStream(wxPayConfig.getPrivateKeyPath()));
            
            // 获取证书管理器实例
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            // 向证书管理器增加需要自动更新平台证书的商户信息
            certificatesManager.putMerchant(wxPayConfig.getMchId(),
                    new WechatPay2Credentials(wxPayConfig.getMchId(),
                            new PrivateKeySigner(wxPayConfig.getSerialNo(), privateKey)),
                    wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
                    
            // 从证书管理器获取验证器
            Verifier verifier = certificatesManager.getVerifier(wxPayConfig.getMchId());
            
            // 验证签名
            String message = timestamp + "\n" + nonce + "\n" + body + "\n";
            return verifier.verify(serialNo, message.getBytes(StandardCharsets.UTF_8), signature);
        } catch (Exception e) {
            logger.error("验证签名异常", e);
            return false;
        }
    }
    
    /**
     * 解密微信支付回调加密数据
     */
    private String decryptResource(String ciphertext, String nonce, String associatedData) throws Exception {
        try {
            // Base64解码密文
            byte[] ciphertextBytes = Base64.getDecoder().decode(ciphertext);
            
            // 构建解密参数
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            SecretKeySpec key = new SecretKeySpec(wxPayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8), "AES");
            GCMParameterSpec spec = new GCMParameterSpec(128, nonce.getBytes(StandardCharsets.UTF_8));
            
            // 初始化解密模式
            cipher.init(Cipher.DECRYPT_MODE, key, spec);
            
            // 如果关联数据不为空，设置关联数据
            if (associatedData != null) {
                cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
            }
            
            // 执行解密
            byte[] decryptedBytes = cipher.doFinal(ciphertextBytes);
            
            // 返回解密后的字符串
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            logger.error("解密微信支付回调数据异常", e);
            throw e;
        }
    }
}
